
Alkaline.Processing.ReferencedValue = function(valueType, value) {
	if (typeof valueType !== 'number')
		throw new Error('ReferencedValue: bad valueType: ' + valueType);
	this.valueType = valueType;
	this.value = value;	// a new object
	this.inheritedPrototype = null;
	this.propertyDescriptors = [];
}

Alkaline.Processing.ReferencedValue.VALUE_TYPE = {
	FUNCTION:	1,
	OBJECT:	2,
	ARRAY:	3,
	REGEXP:	4,
	NUMBER:	5,
	STRING:	6,
	BOOLEAN:	7,
	NULL:		8,
	UNDEFINED:	9,
	UNKNOWN:	10,
	UNKNOWN_NATIVE: 11,
	ARGUMENTS_ARRAY: 12
}

Alkaline.Processing.ReferencedValue.prototype.toString = function() {
	return 'Value(' + this.valueType + '): ' + this.value;
}

Alkaline.Processing.ReferencedValue.prototype.toAST = function(builder) {
	var TYPE = Alkaline.Processing.ReferencedValue.VALUE_TYPE;
	switch (this.valueType) {
		case TYPE.NUMBER:
			return builder.Number(this.value);
		case TYPE.STRING:
			return builder.String(this.value);
		case TYPE.BOOLEAN:
			return this.value ? builder.True() : builder.False();
		case TYPE.ARGUMENTS_ARRAY:
			return builder.Id('arguments');
		case TYPE.OBJECT:
			if (this.value === null)
				return builder.Null();
	}
	throw new Error("[ReferencedValue->toAST] Not supported for this value! " + this.toString());
}

Alkaline.Processing.ReferencedValue.prototype._toPrimitive = function(unknown) {
	var vt = Alkaline.Processing.ReferencedValue.VALUE_TYPE;
	switch (this.valueType) {
		case vt.NUMBER:
		case vt.STRING:
		case vt.BOOLEAN:
			if (this.value !== null)
				return this.value;
			break;
	}
	// for other objects we should try and invoke valueOf() method and if that does not return a native type then toString()
	// if toString() does not return a native type either => TypeError
	return unknown;
}

Alkaline.Processing.ReferencedValue.prototype.toNumber = function(forceNewNumber) {
	var newVal = null;
	var vt = Alkaline.Processing.ReferencedValue.VALUE_TYPE;
	switch (this.valueType) {
		case vt.STRING:
			if (this.value !== null)
				newVal = +this.value;
			break;
		case vt.BOOLEAN:
			if (this.value !== null)
				newVal = +this.value;
			break;
		case vt.NUMBER:
			if (forceNewNumber)
				return new Alkaline.Processing.ReferencedValue(vt.NUMBER, this.value);
			return this;
		case vt.UNKNOWN:
		case vt.UNKNOWN_NATIVE:
		case vt.ARGUMENTS_ARRAY:
		case vt.FUNCTION:
		case vt.OBJECT:
		case vt.ARRAY:
		case vt.REGEXP:
			break;
	}
	return new Alkaline.Processing.ReferencedValue(vt.NUMBER, newVal);
}

Alkaline.Processing.ReferencedValue.prototype.isSimpleValue = function() {
	switch (this.valueType) {
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.NUMBER:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.STRING:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.BOOLEAN:
			return this.value !== null;
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.ARGUMENTS_ARRAY:
			return true;
	}
	return false;
}

Alkaline.Processing.ReferencedValue.prototype.getPropertyDescriptor = function(condition, name) {
	var p = Alkaline.Processing;
	var list = { conditions: [], values: [] };
	var startVal;
	if (this.valueType === p.ReferencedValue.VALUE_TYPE.UNKNOWN)
			startVal = new p.ReferencedValue(p.ReferencedValue.VALUE_TYPE.UNKNOWN, {});
		else
			startVal = new p.ReferencedValue(p.ReferencedValue.VALUE_TYPE.UNKNOWN_NATIVE);
	startVal = new p.ValueDistribution([{ condition: p.Condition.ALWAYS, value: startVal }]);
	list.conditions.push(condition);
	list.values.push(new p.PropertyDescriptor(this, name, condition, startVal));
	return new p.ValueDistribution(list);
}

Alkaline.Processing.ReferencedValue.prototype.equals = function(otherValue) {
	if (this === otherValue)
		return true;
	if (this.valueType !== otherValue.valueType)
		return false;
	switch (this.valueType) {
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.NUMBER:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.STRING:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.BOOLEAN:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.OBJECT:
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.ARRAY:
			return this.value !== null && this.value === otherValue.value;
		case Alkaline.Processing.ReferencedValue.VALUE_TYPE.NULL:
			return true;
	}
	return false;
}

Alkaline.Processing.ReferencedValue.primitives = {
	True: new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.BOOLEAN, true),
	False: new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.BOOLEAN, false),
	Null: new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.NULL, null),
	Undefined: new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.UNDEFINED, null)
}
